package com.edouardbataille.open.requester
{
	import __AS3__.vec.Vector;
	
	import com.edouardbataille.open.MySqlSocket;
	import com.edouardbataille.open.MysqlConst;
	import com.edouardbataille.open.handler.StatementOkHandler;
	import com.edouardbataille.open.handler.StatementResultHandler;
	import com.edouardbataille.open.mysql;
	import com.edouardbataille.open.sender.Sender;
	
	import flash.utils.ByteArray;
	
	import mx.managers.CursorManager;
	import mx.rpc.AsyncToken;
	import mx.rpc.Fault;
	import mx.rpc.events.FaultEvent;

	public class PreparedStatement extends Sender
	{
		private var _statementId : uint;

		private var args : Vector.<Field> = new Vector.<Field>();

		private var acutalResponse : ByteArray;
		
		private var doExecute : Boolean = false;
		
		private var alreadySetBusyCursor : Boolean = false;
		
		mysql function set statementId (value : uint) : void
		{
			_statementId = value;
			handler = new StatementResultHandler();
			if (doExecute)
				execute();
		}
		
		public function PreparedStatement (con : MySqlSocket)
		{
			super(con);
			handler = new StatementOkHandler(this);
		}

		public function set query (value : String) : void
		{
			super.queryStr = value;
			var response : ByteArray = new ByteArray();
			response.writeByte(MysqlConst.COM_STMT_PREPARE);
			response.writeMultiByte(value, "latin1");
			send(response);
		}

		public function execute () : AsyncToken
		{
			if (showBusyCursor && !alreadySetBusyCursor)
			{
				CursorManager.setBusyCursor();
				alreadySetBusyCursor = true;
			}
			if (handler == null)
			{
				var faultString : String = "Can't execute a closed statement";
				if (hasEventListener(FaultEvent.FAULT))
				{
					var fault : Fault = new Fault ("statement closed", faultString);
					dispatchEvent(FaultEvent.createEvent(fault));
					return null;
				}
				else
				{
					// Throw an error because nobody listen Fault event
					// And assigning to AsyncToken is impossible because we are in a sync call 
					throw new Error (faultString);
				}
			}
			if (!token)
				token = new AsyncToken ();
			if (!(handler is StatementResultHandler))
			{
				doExecute = true;
				return token;
			}
			alreadySetBusyCursor = false;
			doExecute = false;
			var response : ByteArray = new ByteArray();
			//command
			response.writeByte(MysqlConst.COM_STMT_EXECUTE);

			// statement id
			response.writeInt(_statementId);

			// cursor
			response.writeByte(0);

			//reserved
			response.writeInt(1);

			// null parameter
			writeNull (response);

			// parameter change
			response.writeByte(1);

			addFieldType(response);
			addFieldValue(response);
			
			send(response);
			return token;
		}
		
		private function writeNull (data : ByteArray) : void
		{
			var j : uint = 0;
			var res : uint = 0;
			var written : Boolean = false;
			for (var i : int = 0; i < args.length; ++i)
			{
				if (args[i].value == null)
					res |= (1 & 0x1) << j;
				++j
				written = false;
				if (j == 7)
				{
					data.writeByte(res);
					res = j = 0;
					written = true;
				}
				
			}
			if (!written)
			{
				data.writeByte(res);
			}
		}
		
		private function addFieldType (data : ByteArray) : void
		{
			for (var i : int = 0; i < args.length; ++i)
			{
				data.writeByte(args[i].type);
				data.writeByte(0);
			}
		}
		
		private function addFieldValue (data : ByteArray) : void
		{
			for (var i : int = 0; i < args.length; ++i)
			{
				if (args[i].value == null)
					continue;
				var length : uint;
				switch (args[i].type)
				{
					case MysqlConst.FIELD_TYPE_LONG:
						var value : int = args[i].value;
						data.writeByte(value & 0xFF);
						data.writeByte((value >> 8) & 0xFF);
						data.writeByte((value >> 16) & 0xFF);
						data.writeByte((value >> 24) & 0xFF);
						break;
					case MysqlConst.FIELD_TYPE_VARCHAR:
						var str : String = args[i].value;
						length = str.length;
						data.writeByte(length);
						data.writeMultiByte(str, "latin1");
						break;
					case MysqlConst.FIELD_TYPE_TINY:
						var bValue : Boolean = args[i].value;
						data.writeByte(value ? 1 : 0);
						break;
					case MysqlConst.FIELD_TYPE_DATE:
						length = 4;
						writeDate(args[i].value as Date, length, data);
						break;
					case MysqlConst.FIELD_TYPE_DATETIME:
						length = 7;
						writeDate(args[i].value as Date, length, data);
						writeTime(args[i].value, data);
						break;
					case MysqlConst.FIELD_TYPE_TIME:
						writeLengthTime(args[i].value, data);
						break;
				}
			}
		}
		
		private function writeDate (date : Date, length : uint, data : ByteArray) : uint
		{
			var year : uint = date.getFullYear();
			var month : uint = date.getMonth() + 1;
			var day : uint = date.getDate();
			
			data.writeByte(length);
			data.writeByte(year & 0xFF);
			data.writeByte((year >> 8) & 0xFF);
			data.writeByte(month);
			data.writeByte(day);
			
			return length - 4;
		}
		
		private function writeLengthTime (date : Date, data : ByteArray) : void
		{
			var length : uint = 10; 
			data.writeByte(length);
			data.writeByte(0); // sign
			var day : uint = date.getDate();
			data.writeByte(day & 0xFF);
			data.writeByte((day >> 8) & 0xFF);
			data.writeByte((day >> 16) & 0xFF);
			data.writeByte((day >> 24) & 0xFF);
			writeTime(date, data);
		}
		
		private function writeTime (date : Date, data : ByteArray) : void
		{
			var hours : uint = date.getHours();
			var minutes : uint = date.getMinutes();
			var seconds : uint = date.getSeconds();
			
			data.writeByte(hours);
			data.writeByte(minutes);
			data.writeByte(seconds);
		}

		public function setInt (pos : int, value : int) : void
		{
			args[pos] = new Field (MysqlConst.FIELD_TYPE_LONG, value);
		}
		
		public function setString (pos : int, value : String) : void
		{
			args[pos] = new Field (MysqlConst.FIELD_TYPE_VARCHAR, value);
		}
		
		public function setBoolean (pos : int, value : Boolean) : void
		{
			args[pos] = new Field (MysqlConst.FIELD_TYPE_TINY, value);
		}
		
		public function setDate (pos : int, value : Date) : void
		{
			args[pos] = new Field (MysqlConst.FIELD_TYPE_DATE, value);
		} 
		
		public function setDateTime (pos : int, value : Date) : void
		{
			args[pos] = new Field (MysqlConst.FIELD_TYPE_DATETIME, value);
		} 
		
		public function setTime (pos : int, value : Date) : void
		{
			args[pos] = new Field (MysqlConst.FIELD_TYPE_TIME, value);
		} 
		
		public function setNull (pos : int) : void
		{
			args[pos] = new Field (0, null);
		}
		
		public function close () : void
		{
			var response : ByteArray = new ByteArray ();
			response.writeByte(MysqlConst.COM_STMT_CLOSE);
			response.writeInt(_statementId);
			send (response);
			handler = null;
		}

	}
}